//
//  Springies.m
//  PezMonkey
//
//  Created by Spence DiNicolantonio on 12/23/09.
//  Copyright 2009 Necrosoft. All rights reserved.
//

#import "Springies.h"


@implementation Springies

- (NSString *)demoName {
	return @"Springies";
}

- (void)start {
	// create space
	self.space = [PMSpace space];
	[space setSubSteps:3];
	[space setIterations:3];
	
	// create shared static body
	PMBody *staticBody = [PMBody staticBody];
	
	// create bars
	PMBody *body1  = [self addBarWithStart:PMVectMake(-240,  160) end:PMVectMake(-160,   80) group:1];
	PMBody *body2  = [self addBarWithStart:PMVectMake(-160,   80) end:PMVectMake( -80,  160) group:1];
	PMBody *body3  = [self addBarWithStart:PMVectMake(   0,  160) end:PMVectMake(  80,    0) group:0];
	PMBody *body4  = [self addBarWithStart:PMVectMake( 160,  160) end:PMVectMake( 240,  160) group:0];
	PMBody *body5  = [self addBarWithStart:PMVectMake(-240,    0) end:PMVectMake(-160,  -80) group:2];
	PMBody *body6  = [self addBarWithStart:PMVectMake(-160,  -80) end:PMVectMake( -80,    0) group:2];
	PMBody *body7  = [self addBarWithStart:PMVectMake( -80,    0) end:PMVectMake(   0,    0) group:2];
	PMBody *body8  = [self addBarWithStart:PMVectMake(   0,  -80) end:PMVectMake(  80,  -80) group:0];
	PMBody *body9  = [self addBarWithStart:PMVectMake( 240,   80) end:PMVectMake( 160,    0) group:3];
	PMBody *body10 = [self addBarWithStart:PMVectMake( 160,    0) end:PMVectMake( 240,  -80) group:3];
	PMBody *body11 = [self addBarWithStart:PMVectMake(-240,  -80) end:PMVectMake(-160, -160) group:4];
	PMBody *body12 = [self addBarWithStart:PMVectMake(-160, -160) end:PMVectMake( -80, -160) group:0];
	PMBody *body13 = [self addBarWithStart:PMVectMake(   0, -160) end:PMVectMake(  80, -160) group:0];
	PMBody *body14 = [self addBarWithStart:PMVectMake( 160, -160) end:PMVectMake( 240, -160) group:0];
	
	// create pivot joints on some bars
	PMConstraint *joint;
	joint = [PMPivotJoint pivotJointWithAnchor:PMVectMake( 40,-40) onBodyA:body1 
										anchor:PMVectMake(-40,-40) onBodyB:body2];
	[space addConstraint:joint];
	joint = [PMPivotJoint pivotJointWithAnchor:PMVectMake( 40,-40) onBodyA:body5 
										anchor:PMVectMake(-40,-40) onBodyB:body6];
	[space addConstraint:joint];
	joint = [PMPivotJoint pivotJointWithAnchor:PMVectMake( 40, 40) onBodyA:body6 
										anchor:PMVectMake(-40,  0) onBodyB:body7];
	[space addConstraint:joint];
	joint = [PMPivotJoint pivotJointWithAnchor:PMVectMake(-40,-40) onBodyA:body9 
										anchor:PMVectMake(-40, 40) onBodyB:body10];
	[space addConstraint:joint];
	joint = [PMPivotJoint pivotJointWithAnchor:PMVectMake( 40,-40) onBodyA:body11 
										anchor:PMVectMake(-40,  0) onBodyB:body12];
	[space addConstraint:joint];
	
	
	// create springs attatched to static body
	[self addSpringWithAnchor:PMVectMake(-320,  240) onBodyA:staticBody anchor:PMVectMake(-40, 40) onBodyB:body1];
	[self addSpringWithAnchor:PMVectMake(-320,   80) onBodyA:staticBody anchor:PMVectMake(-40, 40) onBodyB:body1];
	[self addSpringWithAnchor:PMVectMake(-160,  240) onBodyA:staticBody anchor:PMVectMake(-40, 40) onBodyB:body1];
	[self addSpringWithAnchor:PMVectMake(-160,  240) onBodyA:staticBody anchor:PMVectMake( 40, 40) onBodyB:body2];
	[self addSpringWithAnchor:PMVectMake(   0,  240) onBodyA:staticBody anchor:PMVectMake( 40, 40) onBodyB:body2];
	[self addSpringWithAnchor:PMVectMake(  80,  240) onBodyA:staticBody anchor:PMVectMake(-40, 80) onBodyB:body3];
	[self addSpringWithAnchor:PMVectMake(  80,  240) onBodyA:staticBody anchor:PMVectMake(-40,  0) onBodyB:body4];
	[self addSpringWithAnchor:PMVectMake( 320,  240) onBodyA:staticBody anchor:PMVectMake( 40,  0) onBodyB:body4];
	[self addSpringWithAnchor:PMVectMake(-320,   80) onBodyA:staticBody anchor:PMVectMake(-40, 40) onBodyB:body5];
	[self addSpringWithAnchor:PMVectMake( 320,  80) onBodyA:staticBody anchor:PMVectMake( 40, 40) onBodyB:body9];
	[self addSpringWithAnchor:PMVectMake( 320,   0) onBodyA:staticBody anchor:PMVectMake( 40,-40) onBodyB:body10];
	[self addSpringWithAnchor:PMVectMake( 320,-160) onBodyA:staticBody anchor:PMVectMake( 40,-40) onBodyB:body10];
	[self addSpringWithAnchor:PMVectMake(-320,-160) onBodyA:staticBody anchor:PMVectMake(-40, 40) onBodyB:body11];
	[self addSpringWithAnchor:PMVectMake(-240,-240) onBodyA:staticBody anchor:PMVectMake(-40,  0) onBodyB:body12];
	[self addSpringWithAnchor:PMVectMake(   0,-240) onBodyA:staticBody anchor:PMVectMake( 40,  0) onBodyB:body12];
	[self addSpringWithAnchor:PMVectMake(   0,-240) onBodyA:staticBody anchor:PMVectMake(-40,  0) onBodyB:body13];
	[self addSpringWithAnchor:PMVectMake(  80,-240) onBodyA:staticBody anchor:PMVectMake( 40,  0) onBodyB:body13];
	[self addSpringWithAnchor:PMVectMake(  80,-240) onBodyA:staticBody anchor:PMVectMake(-40,  0) onBodyB:body14];
	[self addSpringWithAnchor:PMVectMake( 240,-240) onBodyA:staticBody anchor:PMVectMake( 40,  0) onBodyB:body14];
	[self addSpringWithAnchor:PMVectMake( 320,-160) onBodyA:staticBody anchor:PMVectMake( 40,  0) onBodyB:body14];
	
	[self addSpringWithAnchor:PMVectMake( 40,-40) onBodyA:body1 anchor:PMVectMake(-40, 40) onBodyB:body5];
	[self addSpringWithAnchor:PMVectMake( 40,-40) onBodyA:body1 anchor:PMVectMake( 40, 40) onBodyB:body6];
	[self addSpringWithAnchor:PMVectMake( 40, 40) onBodyA:body2 anchor:PMVectMake(-40, 80) onBodyB:body3];
	[self addSpringWithAnchor:PMVectMake(-40, 80) onBodyA:body3 anchor:PMVectMake(-40,  0) onBodyB:body4];
	[self addSpringWithAnchor:PMVectMake( 40,-80) onBodyA:body3 anchor:PMVectMake(-40,  0) onBodyB:body4];
	[self addSpringWithAnchor:PMVectMake( 40,-80) onBodyA:body3 anchor:PMVectMake( 40,  0) onBodyB:body7];
	[self addSpringWithAnchor:PMVectMake(-40, 80) onBodyA:body3 anchor:PMVectMake(-40,  0) onBodyB:body7];
	[self addSpringWithAnchor:PMVectMake( 40,-80) onBodyA:body3 anchor:PMVectMake( 40,  0) onBodyB:body8];
	[self addSpringWithAnchor:PMVectMake( 40,-80) onBodyA:body3 anchor:PMVectMake(-40,-40) onBodyB:body9];
	[self addSpringWithAnchor:PMVectMake( 40,  0) onBodyA:body4 anchor:PMVectMake( 40, 40) onBodyB:body9];
	[self addSpringWithAnchor:PMVectMake(-40, 40) onBodyA:body5 anchor:PMVectMake(-40, 40) onBodyB:body11];
	[self addSpringWithAnchor:PMVectMake( 40,-40) onBodyA:body5 anchor:PMVectMake( 40,-40) onBodyB:body11];
	[self addSpringWithAnchor:PMVectMake( 40,  0) onBodyA:body7 anchor:PMVectMake(-40,  0) onBodyB:body8];
	[self addSpringWithAnchor:PMVectMake(-40,  0) onBodyA:body8 anchor:PMVectMake( 40,  0) onBodyB:body12];
	[self addSpringWithAnchor:PMVectMake(-40,  0) onBodyA:body8 anchor:PMVectMake(-40,  0) onBodyB:body13];
	[self addSpringWithAnchor:PMVectMake( 40,  0) onBodyA:body8 anchor:PMVectMake( 40,  0) onBodyB:body13];
	[self addSpringWithAnchor:PMVectMake( 40,  0) onBodyA:body8 anchor:PMVectMake(-40,  0) onBodyB:body14];
	[self addSpringWithAnchor:PMVectMake( 40,-40) onBodyA:body10 anchor:PMVectMake(-40,  0) onBodyB:body14];
	[self addSpringWithAnchor:PMVectMake( 40,-40) onBodyA:body10 anchor:PMVectMake(-40,  0) onBodyB:body14];
}
	
/**
 * Creates and adds a new bar with given attributes to the space and returns the
 * corresponding PMBody object.
 */
- (PMBody *)addBarWithStart:(PMVect)start end:(PMVect)end group:(PMCollisionGroup)group {
	if (!self.space)
		return nil;
	
	// define bar attributes
	PMVect center = PMVectMult(PMVectAdd(start, end), 0.5);
	PMFloat length = PMVectDist(end, start);
	PMFloat width = 20;
	PMFloat mass = length / 160.0;
	PMFloat moment = mass * length * length / 12.0;
	
	// create bar body
	PMBody *body = [PMBody bodyWithMass:mass moment:moment];
	[body setPosition:center];
	[space addBody:body];
	
	// create bar shape
	PMShape *shape = [PMSegmentShape segmentShapeWithBody:body 
													start:PMVectSub(start, center) 
													  end:PMVectSub(end, center)
													width:width];
	[shape setCollisionGroup:group];
	[space addShape:shape];
	
	// return the body of the created bar
	return [body retain];
}

/**
 * Creates and adds a new spring to the space, connecting two given bodies at
 * given anchor points.
 */
- (void)addSpringWithAnchor:(PMAnchor)anchorA onBodyA:(PMBody *)aBody anchor:(PMAnchor)anchorB onBodyB:(PMBody *)otherBody {
	// define spring attributes
	PMFloat restLength = 0.0;
	PMFloat stiffness = 30.0;
	PMFloat damping = 1.3;
	
	// create spring
	PMConstraint *spring = [PMDampedSpring dampedSpringWithAnchor:anchorA 
														  onBodyA:aBody 
														   anchor:anchorB 
														  onBodyB:otherBody 
													   restLength:restLength 
														stiffness:stiffness 
														  damping:damping];
	[space addConstraint:spring];
}

@end
